﻿using DataType.CustomEnumType;
using MapCalclator;
using MathNet.Numerics;
using OprsSimulator.Operators;
using OprsSimulator.OprAttributes;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.NetworkInformation;

namespace ActionsJudge.JudgeMethods
{
    public class Flight_AD_hoc_network
    {
        public float Channel_model(int sender_pos, int reciever_pos, int sender_height, int reciever_height, float fading_gain, float a, float C_send, float C_recieve, Map map)
        {
            int row1 = (int)(sender_pos / 100);
            int col1 = (int)(sender_pos % 100);
            int sender_height1 = map[row1, col1].elev + sender_height;

            int row2 = (int)(reciever_pos / 100);
            int col2 = (int)(reciever_pos % 100);
            int reciever_height1 = map[row2, col2].elev + reciever_height;

            float distance = (float)Math.Sqrt(Math.Pow(map.GetDistanceInMeter(sender_pos, reciever_pos), 2) + (float)Math.Pow(sender_height1 - reciever_height1, 2));

            float G = C_send * C_recieve * (float)Math.Pow(Math.Abs(fading_gain), a) / distance;
            return G;
        }

        public Tuple<float, float> Calculate_SNR(int P, float N0, int sender_pos, int reciever_pos, int sender_height, int reciever_height, float fading_gain, float a, float C_send, float C_recieve, int B, Map map)
        {
            float G = Channel_model(sender_pos, reciever_pos, sender_height, reciever_height, fading_gain, a, C_send, C_recieve, map);
            float SNR = P * G / (N0 * B);
            float SNR_dB = 10 * (float)Math.Log10(SNR);
            return Tuple.Create(SNR_dB, SNR);
        }

        public float Calculate_SNR_Throughput(int B, int P, float N0, int sender_pos, int reciever_pos, int sender_height, int reciever_height, float fading_gain, float a, float C_send, float C_recieve, int Tm, Map map)
        {
            Tuple<float, float> snrTuple = Calculate_SNR(P, N0, sender_pos, reciever_pos, sender_height, reciever_height, fading_gain, a, C_send, C_recieve, B, map);
            float SNR_dB = snrTuple.Item1;

            if (SNR_dB > Tm)
            {
                float SNR = snrTuple.Item2;
                float throughput = B * (float)Math.Log(1 + SNR, 2);
                return throughput;
            }
            else
            {
                return 0;
            }
        }

        public Tuple<float, float> Calculate_Delay(int num_packet, int id1, int id2, int B, int P, float N0, int sender_pos, int reciever_pos, int sender_height, int reciever_height, float fading_gain, float a, float C_send, float C_recieve, int num_byte, int Tm, Map map)
        {
            float throughput = Calculate_SNR_Throughput(B, P, N0, sender_pos, reciever_pos, sender_height, reciever_height, fading_gain, a, C_send, C_recieve, Tm, map);

            if (throughput == 0)
            {
                // Console.WriteLine($"无人机{id1}到无人机{id2}通信失败");
                float delay = float.PositiveInfinity;
                float trans_delay = float.PositiveInfinity;
                return Tuple.Create(delay, trans_delay);
            }
            else
            {
                float trans_delay = num_byte * 8 / throughput;
                float delay = num_packet * num_byte * 8 / throughput;
                return Tuple.Create(delay, trans_delay);
            }
        }

        public float CalculateSuccessProbability(float fadingGain, int Tm, float N0, int senderPos, int receiverPos, int senderHeight, int receiverHeight, float a, float cSend, float cReceive, int P, int B, Map map)
        {
            // int row1, col1, row2, col2;
            // map.DividePos(senderPos, out row1, out col1);
            // map.DividePos(receiverPos, out row2, out col2);
            int row1 = (int)(senderPos / 100);
            int col1 = (int)(senderPos % 100);
            int row2 = (int)(receiverPos / 100);
            int col2 = (int)(receiverPos % 100);

            float senderHeight1 = map[row1, col1].elev + senderHeight;
            float receiverHeight1 = map[row2, col2].elev + receiverHeight;

            float distance = (float)Math.Sqrt(Math.Pow(map.GetDistanceInMeter(senderPos, receiverPos), 2) + (float)Math.Pow(senderHeight1 - receiverHeight1, 2));

            Tuple<float, float> snrResult = Calculate_SNR(P, N0, senderPos, receiverPos, senderHeight, receiverHeight, fadingGain, a, cSend, cReceive, B, map);
            float SNRdB = snrResult.Item1;
            float SNR = snrResult.Item2;
            if (SNRdB > Tm)
            {
                float successProbability = (float)Math.Exp(-((N0 * B * Tm * Math.Pow(distance, a)) / (cSend * cReceive * P)));
                return successProbability;
            }
            else
            {
                return 0;
            }
        }

        public Tuple<float[], int> CalculatePacketLossRate(int P, float N0, int senderPos, int receiverPos, int senderHeight, int receiverHeight, float fadingGain, float a, float CSend, float CReceive, int numPacket, int lossByteTm, int numByte, int B, Map map)
        {
            int lossPacket = 0;
            Tuple<float, float> snrResult = Calculate_SNR(P, N0, senderPos, receiverPos, senderHeight, receiverHeight, fadingGain, a, CSend, CReceive, B, map); // 计算信噪比
            float snr = snrResult.Item2;

            float[] byteErrorProbability = new float[numByte];
            for (int j = 0; j < numByte; j++)
            {
                float berTheoretical = 0.5f * (float)SpecialFunctions.Erfc(Math.Sqrt(snr / 2.0)); // Q 函数的表达式计算误比特率
                float bitErrorProbability = 1 - (float)Math.Pow(1 - berTheoretical, 8); // 根据误比特率计算一个字节中至少有一个 bit 错误的概率
                byteErrorProbability[j] = (float)MathNet.Numerics.Distributions.Binomial.PMF(bitErrorProbability, numByte, j); // 二项分布计算每个字节错误概率
            }

            Random random = new Random();

            for (int i = 0; i < numPacket; i++)
            {
                int lossByte = 0;
                for (int j = 0; j < numByte; j++)
                {
                    if (random.NextDouble() < byteErrorProbability[j])
                    {
                        lossByte++; // 根据每个字节错误概率预测一个包中字节错误的个数
                    }
                }

                if (lossByte > lossByteTm)
                {
                    lossPacket++; // 若一个包中字节错误个数 > lossByteTm，则发生丢包
                }
            }

            return Tuple.Create(byteErrorProbability, lossPacket);
        }

        public Tuple<List<int>, List<int>> AdovRouting(float[,] throughputMatrix, int source, int destination, int numPacket, List<int> oprId)
        {
            int sourceNode = oprId.IndexOf(source) + 1;
            int destinationNode = oprId.IndexOf(destination) + 1;
            int numNodes = throughputMatrix.GetLength(0);
            float[,] delayMatrix = new float[numNodes, numNodes];
            List<int> route = new List<int>();
            List<int> routeIds = new List<int>();
            // 计算时延矩阵
            for (int i = 0; i < numNodes; i++)
            {
                for (int j = 0; j < numNodes; j++)
                {
                    if (i != j && throughputMatrix[i, j] != 0)
                    {
                        delayMatrix[i, j] = numPacket * 80 * 8 / throughputMatrix[i, j];
                    }
                    else
                    {
                        delayMatrix[i, j] = float.PositiveInfinity;
                    }
                }
            }

            int[,] pathMatrix = new int[numNodes, numNodes]; //初始化路径矩阵
            // ADOV路由过程
            for (int k = 0; k < numNodes; k++)
            {
                for (int i = 0; i < numNodes; i++)
                {
                    for (int j = 0; j < numNodes; j++)
                    {
                        if (delayMatrix[i, k] + delayMatrix[k, j] < delayMatrix[i, j] && i != j)//求每两点之间最小开销的中间节点
                        {
                            delayMatrix[i, j] = delayMatrix[i, k] + delayMatrix[k, j];//将两点间算出的最小开销赋值给时延矩阵，继续找中间节点
                            pathMatrix[i, j] = k + 1;//节点从1开始编号
                        }
                    }
                }
            }
            // 构建路由路径
            route.Add(sourceNode);
            int currentNode = sourceNode - 1;//序号从0开始，current_node指的是矩阵中的序号
            int nextNode = 0;
            int nextNodeS = 0;

            while (currentNode != destinationNode - 1)//当当前路由路径的节点编号不为目的节点编号时
            {
                nextNode = pathMatrix[currentNode, destinationNode - 1];//找当前路由到的节点与目的节点指的最小开销的中间节点

                if (nextNode == 0)//若中间节点为自身
                {
                    break;//停止找路
                }

                while (nextNodeS != sourceNode)//当下一跳不为源节点
                {
                    if (pathMatrix[currentNode, nextNode - 1] != 0)//若当前跳不为刚刚寻找到的中间节点
                    {
                        nextNodeS = pathMatrix[currentNode, nextNode - 1];//则寻找当前跳与中间节点的最小开销的中间节点
                        route.Add(nextNodeS);//储存当前跳与刚刚寻找到的中间节点的中间节点（重复寻找）
                        currentNode = nextNodeS - 1;//跳到中间节点的位置
                    }
                    else
                    {
                        break;//当下一跳为源节点,退出
                    }
                }

                route.Add(nextNode);//将下一节点储存进路由路径中
                currentNode = nextNode - 1;//跳到寻找到的下一跳
            }

            route.Add(destinationNode);

            while (route.Contains(0))
            {
                route.Remove(0);
            }

            if (route.Count == 2 && throughputMatrix[sourceNode - 1, destinationNode - 1] == 0)
            {
                route = new List<int>();
                route.Add(0);
                routeIds = new List<int>();
                routeIds.Add(0);//如果源节点到目的节点都是断路，则没有路径
            }
            else
            {
                foreach (int nodeId in route)
                {
                    routeIds.Add(oprId[nodeId - 1]);
                }

                // Console.WriteLine($"无人机{source}到无人机{destination}间路由为{string.Join("->", routeIds)}");

            }



            return Tuple.Create(routeIds, route);
        }

        public Tuple<bool, bool, float, int, List<int>> Communication(
        List<IOperator> oprs,
        int source,
        int destination,
        int N0_dB,
        float fading_gain,
        float a,
        int C_send_dB,
        int C_receive_dB,
        int Tm,
        int num_packet,
        int loss_byte_Tm,
        int num_byte,
        Map map)
        {
            float time = 0;
            List<int> ROUTE = new List<int>();
            List<int> opr_id = new List<int>();
            float C_send = (float)Math.Pow(10, C_send_dB / 10);
            float C_receive = (float)Math.Pow(10, C_receive_dB / 10);
            float N0 = (float)Math.Pow(10, N0_dB / 10);
            int num_packet_s = num_packet;
            float[,] throughput_matrix = new float[oprs.Count, oprs.Count];
            float[,] count = new float[oprs.Count, oprs.Count];
            for (int i = 0; i < oprs.Count; i++)
            {
                opr_id.Add(oprs[i].ObjID);
            }
            List<List<int>> ROUTEList = new List<List<int>>();
            foreach (IOperator opr in oprs)
            {
                if (!typeof(ICommunicationAttris).IsAssignableFrom(opr.GetType())) continue;
                CommDroneOpr commOpr = new CommDroneOpr();
                List<int> opr_route = (List<int>)opr.GetType().GetProperty(nameof(commOpr.Route)).GetValue(opr);
                if (opr_route != null)
                {
                    if (opr_route.Count > 0)
                    {
                        opr_route = opr_route.Select(element => opr_id.IndexOf(element) + 1).ToList();
                        ROUTEList.Add(opr_route);
                    }
                }
            }

            if (ROUTEList.Count > 0)
            {
                foreach (List<int> A in ROUTEList)
                {
                    for (int j = 0; j < A.Count - 1; j++)
                    {
                        int j_s = A[j];
                        int j_d = A[j + 1];
                        count[j_s - 1, j_d - 1] += 1;
                    }
                }
            }

            for (int i = 0; i < oprs.Count; i++)
            {
                for (int j = 0; j < oprs.Count; j++)
                {
                    if (i != j)
                    {
                        if (!typeof(ICommunicationAttris).IsAssignableFrom(oprs[i].GetType())) continue;
                        CommDroneOpr commOpr = new CommDroneOpr();
                        int opr_i_B = (int)oprs[i].GetType().GetProperty(nameof(commOpr.B)).GetValue(oprs[i]);
                        int opr_i_P = (int)oprs[i].GetType().GetProperty(nameof(commOpr.P)).GetValue(oprs[i]);
                        int opr_i_CurHex = (int)oprs[i].GetType().GetProperty(nameof(commOpr.CurHex)).GetValue(oprs[i]);
                        int opr_i_Height = (int)oprs[i].GetType().GetProperty(nameof(commOpr.Height)).GetValue(oprs[i]);

                        int opr_j_CurHex = (int)oprs[j].GetType().GetProperty(nameof(commOpr.CurHex)).GetValue(oprs[j]);
                        int opr_j_Height = (int)oprs[j].GetType().GetProperty(nameof(commOpr.Height)).GetValue(oprs[j]);
                        throughput_matrix[i, j] = Calculate_SNR_Throughput(
                            opr_i_B, opr_i_P, N0,
                            opr_i_CurHex, opr_j_CurHex,
                            opr_i_Height, opr_j_Height,
                            fading_gain, a, C_send, C_receive, Tm, map);
                        throughput_matrix[i, j] /= count[i, j];
                    }
                }
            }

            Tuple<List<int>, List<int>> adovResult = AdovRouting(throughput_matrix, source, destination, num_packet, opr_id);

            List<int> Route = adovResult.Item1;
            List<int> route = adovResult.Item2;

            bool is_success;
            bool is_actionfeasible;
            float remaining_transfer_time = 0;
            int num_packet_received = 0;

            if (route.Count == 1 && route[0] == 0)
            {
                // Console.WriteLine($"无人机{source}到无人机{destination}的通信无有效路径");
                is_success = true;
                is_actionfeasible = false;
                Route = new List<int>();
            }
            else
            {
                is_success = true;
                is_actionfeasible = true;
                remaining_transfer_time = 0;
                num_packet_received = num_packet_s;

                for (int i = 0; i < route.Count - 1; i++)
                {
                    int i_s = route[i];
                    int i_d = route[i + 1];

                    if (!typeof(ICommunicationAttris).IsAssignableFrom(oprs[i_s - 1].GetType())) continue;
                    CommDroneOpr commOpr = new CommDroneOpr();
                    int opr_i_s_B = (int)oprs[i_s - 1].GetType().GetProperty(nameof(commOpr.B)).GetValue(oprs[i_s - 1]);
                    int opr_i_s_P = (int)oprs[i_s - 1].GetType().GetProperty(nameof(commOpr.P)).GetValue(oprs[i_s - 1]);
                    int opr_i_s_CurHex = (int)oprs[i_s - 1].GetType().GetProperty(nameof(commOpr.CurHex)).GetValue(oprs[i_s - 1]);
                    int opr_i_s_Height = (int)oprs[i_s - 1].GetType().GetProperty(nameof(commOpr.Height)).GetValue(oprs[i_s - 1]);

                    int opr_i_d_CurHex = (int)oprs[i_d - 1].GetType().GetProperty(nameof(commOpr.CurHex)).GetValue(oprs[i_d - 1]);
                    int opr_i_d_Height = (int)oprs[i_d - 1].GetType().GetProperty(nameof(commOpr.Height)).GetValue(oprs[i_d - 1]);
                    float success_probability = CalculateSuccessProbability(
                        fading_gain, Tm, N0, opr_i_s_CurHex,
                        opr_i_d_CurHex, opr_i_s_Height,
                        opr_i_d_Height, a, C_send, C_receive,
                        opr_i_s_P, opr_i_s_B, map);

                    if (new Random().NextDouble() < success_probability)
                    {
                        Tuple<float, float> delayResult = Calculate_Delay(
                            num_packet, oprs[i_s - 1].ObjID, oprs[i_d - 1].ObjID,
                            opr_i_s_B, opr_i_s_P, N0,
                            opr_i_s_CurHex, opr_i_d_CurHex,
                            opr_i_s_Height, opr_i_d_Height,
                            fading_gain, a, C_send, C_receive,
                            num_byte, Tm, map);

                        float delay = delayResult.Item1;

                        if (delay == float.PositiveInfinity)
                        {
                            is_success = true;
                            is_actionfeasible = false; // Communication failed due to SNR threshold
                            Route = new List<int>();
                            break;
                        }
                        else
                        {
                            time += delay;

                            Tuple<float[], int> lossPacketResult = CalculatePacketLossRate(
                                opr_i_s_P, N0, opr_i_s_CurHex,
                                opr_i_d_CurHex, opr_i_s_Height,
                                opr_i_d_Height, fading_gain, a,
                                C_send, C_receive, num_packet,
                                loss_byte_Tm, num_byte, opr_i_s_B, map);

                            int loss_packet = lossPacketResult.Item2;
                            num_packet -= loss_packet;

                            is_success = true;
                            is_actionfeasible = true;
                        }
                    }
                    else
                    {
                        // Console.WriteLine("Communication interrupted");
                        num_packet = 0;
                        is_success = true;
                        is_actionfeasible = false; // Communication failed due to SNR threshold
                        Route = new List<int>();
                        break;
                    }
                }

                // Console.WriteLine($"Communication time from drone {source} to drone {destination} is {time}s. Number of packets to be transmitted: {num_packet_s}, final received packets: {num_packet}");

                remaining_transfer_time = time;
                num_packet_received = num_packet;
            }
            return Tuple.Create(is_success, is_actionfeasible, remaining_transfer_time, num_packet_received, Route);
        }
    }
}